Nova
A minicomputer line introduced by Data General in 1969, and the mainstay of the company's product line through the decade of the 1970s. The Nova was one of the first 16-bit minicomputers, and at the time it was introduced, it was smaller, faster, and less expensive than competing products. It became popular initially in scientific and process control applications, and then as the available software suite expanded, it gained a market in offices and educational institutions. The Nova line consisted of four basic series. The original systems were known as the 1200 and 800 models, the number referring to the memory bus cycle time (thus, the lower-numbered model was the higher-performance, higher-cost one). The CPU and bus interface were implemented using discrete logic (microprocessors did not exist at the time). Compared to many competing products, Data General had chosen a very large circuit board standard of 15x15 inches, which made it possible to implement the processor on two boards with no need of manual wiring between, reducing assembly costs compared to their competitors. The elimination of hand wiring also improved reliability, and made servicing easier. (Circuit boards were routinely repaired in the field, rather than swapped out.) The Nova II eventually replaced these models, offering a more integrated CPU with more hardware options. The Nova 3 re-implemented the CPU using TTL logic circuits in place of the older types used in the earlier Novas, making higher clock speeds possible. The final series was the Nova 4, which used the same hardware as the original 16-bit Eclipse line. The Nova architecture was a "pure" 16-bit architecture; it addressed memory in whole words, rather than bytes. This greatly sped up memory transfers and instruction fetching, although it did make manipulation of character strings in assembly language programs rather tedious. Four registers (called "accumulators" in DG documentation) were used for most arithmetic operations, and most operations were register-to-register, with only a few instructions in the instruction set that accessed memory. The instruction set was both orthogonal in that most instructions could use any register, and very minimal. The minimal instruction set allowed for faster execution since no instruction required more a handful of CPU cycles (for instance, a maximum of five in the Nova II). In fact, this is the basic premise of a RISC instruction set, and the Nova instruction set anticipated RISC fifteen years prior to that concept being elaborated in the literature. Addition and subtraction were done in twos complement, and memory access was big endian. The memory access system was designed around the needs of magnetic core memory, although semiconductor memory was available for some models. With magnetic core memory, reading a memory location causes that location to be erased, so if the content is to be preserved, the value must be written back after being read. This slows down processing if the CPU must wait for the write-back before it can access memory again. DG exploited the write-back cycle by implementing a mini-arithmetic unit on the memory board itself, and some instructions used the memory board's capability to avoid needless write-backs. For instance, the "decrement and skip on zero" (DSZ) instruction decremented a memory location, and then skipped the next instruction if the result was zero. It worked by ordering the memory board to read the memory location, decrement the value, and then send the result to the CPU. The CPU tested the result while the memory board wrote back the decremented value to the memory location. The architecture used channelized I/O, with direct memory access (DMA). Most faster devices used DMA. The CPU used channel I/O instructions to send commands and setup data to the device, which would then use DMA to perform the actual I/O. The DMA was essentially invisible to the CPU; the device wishing to do a DMA transfer would request a DMA cycle, and the CPU would signal when the bus was clear. (Due to the register-to-register instruction set, most instructions had at least one execution cycle where the CPU did not need to be on the bus.) The device then accessed the memory directly, without any CPU intervention at all. DMA devices could use the capabilities of the mini-arithmetic unit on the memory board; for example, an input value could be added to the existing contents of a memory location. This capability was exploited by many process control applications to gather process performance and statistical data with a minimum of performance overhead, since the software did not have to be involved in the data collection. All of the Novas except for the Nova 4 had front panels, and the front panel design was an iconic feature of the Nova series. The Nova 800/1200 and Nova II front panels were also notorious among field service personnel, since they used incandescent lamps soldered into the panel board. A routine and tedious part of any field service call for which the customer had a maintenance contract was to pull the panel board, unsolder all of the burned-out bulbs, and solder in new ones. It was necessary to use the front panel to boot the Nova; on most models, this involved setting the channel number of the boot device in the data switches, and then pressing a switch usually marked PROGRAM LOAD. This started the CPU and transferred control to a small ROM which contained the zero-level boot loader. Most of the Novas had a 15-bit address bus, so the maximum memory on these systems was 64 KB (32 kilowords). Memory was sold in 4, 8, and 16-kiloword boards, and most systems could only accept a maximum of two memory boards. Floating point processing and multiply/divide hardware were options, implemented on separate boards. I/O controller boards were available for a wide variety of devices such as disk drives, tape drivers, serial and parallel ports, and analog data ports. A notable I/O board was the "design your own" board, which was a board containing the bus interface circuitry and then a stripboard area where the customer could add circuitry to control whatever device they wished. Various packaging configurations were sold with between four and 12 slots for memory, I/O, and option boards. Summary of the Nova instruction set The Nova instruction set has four types of instructions: *arithmetic and logical *memory access *transfer of control *I/O All instructions are one word (16 bits) long. A problem this creates for the memory access and transfer of control instructions is that they cannot accommodate an address field large enough to address the entire memory address space. So the instruction set creates four addressing modes: *Mode 0 takes the address field as an absolute address. This is called "page zero" addressing. Since the address field is 8 bits wide, this allows addresses from 0 to 255 to be directly addressed. *Mode 1 uses the current program counter value as an index, and takes the address field to be a signed offset. So, it allows references of up to plus or minus 128 memory words away from the current instruction. *Mode 2 uses accumulator 2 (AC2) as an index register, and adds the address field as a signed offset. *Mode 3 does the same, but using accumlator 3 (AC3). There are four memory access instructions: *LDA -- load accumlator; loads data from a memory location to an accumulator *STA -- store accumlator; stores data from an accumulator to a memory location *ISZ -- increment and skip on zero; increments a memory location and skips the next instruction if the result is zero *DSZ -- decrement and skip on zero; decrements a memory location and skips the next instruction if the result is zero Each of these instructions takes an addressing mode number and an addressing field value; the LDA and STA also take the number of the accumulator to be loaded or stored. In addition, these instructions have an indirect bit; if this bit is set, the target memory location is loaded and its contents are taken to be the address of the memory location to be loaded, stored, incremented, or decremented. Setting of the indirect bit is indicated by the presence of the at-sign ('@') in the instruction. Examples: LDA 0, 6, 2 Loads accumlator zero (AC0) with the contents of the memory location whose address is the value contained in AC2, plus 6. STA 2, LOCA Stores AC2 in the memory locatin that has been labeled LOCA. The assembler looks at the address of LOCA and figures out whether to use mode 0 or mode 1, and computes the value of the address field. If LOCA is not in page zero and is not within 128 words of the current location, the assembler flags an error ISZ@ 0, 3 The memory location whose address is in AC3 is accessed, and its value is taken to be a memory address. The memory location at that address is incremented. If the incremented value is zero, the next instruction is skipped. There are two transfer of control instructions: *JMP -- jump *JSR -- jump subroutine; it does the jump and the return address is placed in AC3. (If the jumped-to subroutine uses AC3, it must save the return address.) The transfer of control instructions take an address field, an addressing mode, and an indirect bit. Examples: JMP FUNCT Jumps to location FUNCT, which must be either in page zero, or within 128 words of the current instruction. The assembler figures out the addressing mode (0 or 1) and the address field offset value. JSR@ SUB1 Jumps to location that is pointed to by the contents of location SUB1; as above, the assembler figures out the addressing mode and address field offset for SUB1. JMP 0,3 Jumps to the location whose address is in AC3. (This is the standard method of returning from a subroutine, assuming that AC3 hasn't been altered.) Note that transfer of control instructions which use the indirect bit allow multiple levels of indirection. When a JMP@ or JSR@ is executed, if the memory address contained in the target location has its high bit set, it assumes that the pointed-to address is another indirect address, and it will perform an additional cycle of indirection. This will repeat until the instruction succeeds in retrieving a jump address that does not have the high bit set. In the earlier Nova models, there is no protection against infinite indirection, which locks up the CPU. The arithmetic and logical instructions all operate between registers. All take a source and destination accumulator number; for two-operand instructions, the second operand is obtained from the destination accumulator. In addition, the instructions take four option fields: The carry field specifies how the carry bit shall be set prior to performing the operation. The options are: O -- set the carry bit; Z -- clear the carry bit, C -- complement (invert) the carry bit; blank -- do not change the carry bit. The shift specifier specifies what type of shift should be performed on the result of the operation. Shifts are circular shifts of 17 bits -- the carry bit participates as the bit "between" the most significant bit and the least significant bit. The shift options are: L -- shift left; R -- shift right; S -- swap bytes of the result (carry does not participate in a swap); blank -- do not shift. The test specifier specifies what type of test should be performed after the operation and the shift are completed. If the tested condition is true, the next instruction will be skipped. The options are: *SZR -- skip if the 16-bit result (excluding the carry) is zero *SNR -- skip if the 16-bit result is non-zero *SZC -- skip if the carry is zero (clear) *SNC -- skip if the carry is non-zero (set) *SEZ -- skip if the carry or the result is zero *SBN -- skip if both the carry and the result are non-zero *SKP -- always skip *blank -- never skip The no-load bit, if set, tells the CPU to discard the result of the computation after performing the specified test. The destination accumulator is never altered when the no-load bit is set. Setting of the no-load bit is indicated by the presence of the pound sign ('#') in the instruction. There are eight arithmetic and logical instructions: *MOV -- move the contents of the source accumulator (AC) to the destination AC *COM -- perform a bitwise invert of the source AC and place the result in the destination AC *NEG -- negate the source AC and place the result in the destination AC *ADD -- add the source AC to the destination AC *ADC -- perform a bitwise invert of the source AC and add the result to the destination AC *INC -- add 1 to the source AC and place the result in the destination AC *SUB -- subtract the source AC from the destination AC *AND -- perform a bitwise AND of the source and destination AC's and place the result in the destination AC The I/O instructions control I/O devices and actually move I/O data for some types of devices (the ones that do not use DMA). By convention, each I/O device controller is expected to have: (1) three 16-bit registers, which are arbitrarily labeled A, B, and C, and (2) two single-bit flags, which are named Busy and Done. The A, B, and C registers may be used to pass data or control information. The device is expected to set its Busy flag while it is performing I/O, and its Done flag when the specified I/O is complete. All devices are assigned a channel number in the range 0-63. This is usually done by setting jumpers on the circuit board. Devices respond only to their specified channel. The I/O instructions take one option field, which can be used to send control signals to the device. The control options are: S -- signals the device to initiate I/O and set its Busy flag; C -- signals the device to stop I/O and to clear its Busy and Done flags; P -- a control signal that tells a device to perform a device-specific special function; blank -- send no control signal. There are eleven I/O instructions: *DOA -- transfers the contents of a specified accumulator (AC) to a device's A register. *DIA -- transfers the contents of a device's A register to the specified AC. *DOB -- transfers the contents of a specified AC to a device's B register. *DIB -- transfers the contents of a device's B register to the specified AC. *DOC -- transfers the contents of a specified AC to a device's C register. *DIC -- transfers the contents of a device's C register to the specified AC. *SKPBN -- skip the next instruction if the device's Busy flag is set. *SKPBZ -- skip the next instruction if the device's Busy flag is clear. *SKPDN -- skip the next instruction if the device's Done flag is set. *SKPDZ -- skip the next instruction if the device's Done flag is clear. *NIO -- "No I/O", a misnomer. This instruction is used to send a control signal to a device without effecting any of the A, B, or C registers. In addition, there are certain special instructions that perform control operations on the CPU. These are actually defined as I/O instructions on channel 63, which loops back to the CPU itself. However, the assembler gives them their own mnenomics for clarity. They are: *INTEN -- enable device interrupts *INTDS -- disable device interrupts *MSKO -- "mask out"; transfers the contents of a specified accumulator to the interrupt mask. Devices can be assigned to certain mask bits so that they will not request interrupts when that bit is set in the mask. This is usually done by setting jumpers on the device's circuit board. *INTA -- when executed during interrupt processing, transfers the channel number of the interrupting device to the specified AC. *READS -- transfers the value contained in the front panel data switches to a specified accumulator. *IORST -- I/O reset. Stops all I/O in progress, clears and disables interrupts, and commands all I/O devices to reset to their power-on state. *HALT -- halts the CPU. The program counter is left pointing to the next instruction to be executed; the CPU can be resumed by pressing the CONTINUE switch on the front panel. In addition, performing a SKPDN/SKPDZ on channel 63 tested for the presence of the power fail interrupt.